﻿using System;
using System.Windows;

namespace Bettzueche.Dampfross
{
    /// <summary>
    /// Hexagon (row = z, col = x, oben flach) mit generischem Inhalt. (als Container für Daten)
    /// </summary>
    /// <typeparam name="T">Typ des Dateninhalts (<c>Content</c>)</typeparam>
    [Serializable]
    public class Hexagon<T> : Hexagon
    {
        public T Content { get; set; }

        public Hexagon() : base(){
        }



    }

    /// <summary>
    /// Ein Hexagon, wahlweise oben spitz oder flach mit geometrischen Angaben.
    /// </summary>
    [Serializable]
    public class Hexagon : ICloneable
    {
        /// <summary>1/2 * Wurzel 3</summary>
        private static readonly double Sqrt3_Div2 = Math.Sqrt(3.0) / 2.0;

        /// <summary>Flag für vertikales Hexagon (oben flach / Standard).</summary>
        public const bool OBEN_FLACH = true;
        /// <summary>Flag für horizontales Hexagon (oben spitz).</summary>
        public const bool OBEN_SPITZ = false;
        /// <summary>Eckwinkel in rad eines flachen Sechsecks. Rechts beginnend (0°) gegen Uzgs.</summary>
        public static readonly double[] ANGLES_FLAT;
        /// <summary>Eckwinkel in rad eines spitzen Sechsecks. Rechtsoben beginnend (30°) gegen Uzgs.</summary>
        public static readonly double[] ANGLES_POINTY;
        ///// <summary>2π</summary>
        //public const double TWO_PI = Math.PI * 2;

        /// <summary>
        /// Gets or sets the koordinate [optional].
        /// </summary>
        /// <value>
        /// The koordinate.
        /// </value>
        public HexKoordinate Koordinate { get; internal protected set; }


        /// <summary>
        /// Gets or sets a value indicating whether [ist oben flach].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [ist oben flach]; otherwise, <c>false</c>.
        /// </value>
        public bool IstObenFlach { get; set; }

        /// <summary>
        /// Gibt oder setzt den Radius (zum Eckpunkt) des Hexagons.
        /// (Zum Zeichnen wichtig; Beeinflusst die anderen geom. Größen)
        /// </summary>
        /// <value>
        /// The size.
        /// </value>
        public double Size { 
            get { return _size; }
            set {
                _size = value < 0 ? -value : value;
                if (IstObenFlach) {
                    Width = _size * 2;
                    Height = Sqrt3_Div2 * Width;
                    DistanceHorizontal = 0.75 * Width;
                    DistanceVertical = Height;
                }
                else {
                    Height = _size * 2;
                    Width = Sqrt3_Div2 * Height;
                    DistanceHorizontal = Width;
                    DistanceVertical = 0.75 * Height;
                }
                
            }
        }
        double _size;

        /// <summary>
        /// Gibt die Breite des Hexagons (wird durch <c>Size</c> bestimmt).
        /// </summary>
        public double Width { get; private set; }

        /// <summary>
        /// Gibt die Höhe des Hexagons (wird durch <c>Size</c> bestimmt).
        /// </summary>
        public double Height { get; private set; }

        /// <summary>
        /// Gibt die horizontale Distanz zwischen zwei Hexagonen (wird durch <c>Size</c> bestimmt).
        /// </summary>
        public double DistanceHorizontal { get; private set; }
        /// <summary>
        /// Gibt die vertikale Distanz zwischen zwei Hexagonen (wird durch <c>Size</c> bestimmt).
        /// </summary>
        public double DistanceVertical { get; private set; }

        static Hexagon() {
            ANGLES_FLAT = new double[6];
            ANGLES_POINTY = new double[ANGLES_FLAT.Length];
            for (int a = 0; a < ANGLES_FLAT.Length; a++) {
                ANGLES_FLAT[a] = (Math.PI / 3.0) * a; // 2π/6 * i
                ANGLES_POINTY[a] = (Math.PI / 3.0) * (a + 0.5);
            }
        }

        /// <summary>
        /// Standard Konstruktor für flache Sechsecke.
        /// </summary>
        public Hexagon() {
            IstObenFlach = Hexagon.OBEN_FLACH;
            // --- ...
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Hexagon"/> class.
        /// </summary>
        /// <param name="isflat">if set to <c>true</c> [isflat], pointy otherwise</param>
        public Hexagon(bool isflat) {
            IstObenFlach = isflat;
        }

        /// <summary>
        /// Gibt die Koordinaten der Eckpunkte, relativ zum Zentrum (0-Punkt).
        /// </summary>
        /// <returns></returns>
        public Vector[] GetCorners() {
            return Hexagon.GetCorners(new Vector(), Size, this.IstObenFlach);
        }
        /// <summary>
        /// Gibt die Koordinaten der Eckpunkte, bezogen auf das angegebene Zentrum.
        /// </summary>
        /// <param name="center">The center.</param>
        /// <returns></returns>
        public Vector[] GetCorners(Vector center) {
            return Hexagon.GetCorners(center, Size, this.IstObenFlach);
        }
        /// <summary>
        /// Gibt die Koordinaten der Eckpunkte, bezogen auf das angegebene Zentrum.
        /// </summary>
        /// <param name="centerX">The center x.</param>
        /// <param name="centerY">The center y.</param>
        /// <returns></returns>
        public Vector[] GetCorners(double centerX, double centerY) {
            return Hexagon.GetCorners(new Vector(centerX, centerY), Size, IstObenFlach);
        }

        /// <summary>
        /// Gibt die Koordinaten der HexagonEcken
        /// </summary>
        /// <param name="center">The center.</param>
        /// <param name="size">The size.</param>
        /// <param name="isFlat">if set to <c>true</c> [is flat].</param>
        /// <returns></returns>
        public static Vector[] GetCorners(Vector center, double size, bool isFlat) {
            Vector[] corners = new Vector[6];
            if (isFlat) {
                for (int a = 0; a < Hexagon.ANGLES_FLAT.Length; a++) {
                    corners[a].X = center.X + size * Math.Cos(ANGLES_FLAT[a]);
                    corners[a].Y = center.Y + size * Math.Sin(ANGLES_FLAT[a]);
                }
            }
            else {
                for (int a = 0; a < Hexagon.ANGLES_POINTY.Length; a++) {
                    corners[a].X = center.X + size * Math.Cos(ANGLES_POINTY[a]);
                    corners[a].Y = center.Y + size * Math.Sin(ANGLES_POINTY[a]);
                }
            }
            return corners;
        }


        object ICloneable.Clone() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns></returns>
        public Hexagon Clone() {
            var clone = new Hexagon(IstObenFlach) { Size = this.Size };
            return clone;
        }

    }
}
